home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
mint
/
mint104s.zoo
/
mint.src
/
signal.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-03-08
|
17KB
|
614 lines
/*
Copyright 1990,1991,1992 Eric R. Smith.
Copyright 1992 Atari Corporation.
All rights reserved.
*/
/* signal.c:: signal handling routines */
#include "mint.h"
void (*sig_routine)(); /* used in intr.s */
short sig_exc; /* used in intr.s */
/*
* killgroup(pgrp, sig): send a signal to all members of a process group
* returns 0 on success, or an error code on failure
*/
long
killgroup(pgrp, sig)
int pgrp, sig;
{
PROC *p;
int found = 0;
TRACE(("killgroup %d %d", pgrp, sig));
if (pgrp < 0)
return EINTRN;
for (p = proclist; p; p = p->gl_next) {
if (p->pgrp == pgrp) {
post_sig(p, sig);
found++;
}
}
if (found) {
check_sigs(); /* see if the current process is affected */
return 0;
}
else {
DEBUG(("killgroup: no processes found"));
return EFILNF;
}
}
/* post_sig: post a signal as being pending. It is assumed that the
caller has already verified that "sig" is a valid signal, and
moreover it is the caller's responsibility to call check_sigs()
if it's possible that p == curproc
*/
void
post_sig(p, sig)
PROC *p;
int sig;
{
ulong sigm;
/* if process is ignoring this signal, do nothing
* also: signal 0 is SIGNULL, and should never be delivered through
* the normal channels (indeed, it's filtered out in dossig.c,
* but the extra sanity check here is harmless). The kernel uses
* signal 0 internally for some purposes, but it is handled
* specially (see supexec() in xbios.c, for example).
*/
if (p->sighandle[sig] == SIG_IGN || sig == 0)
return;
/* if the process is already dead, do nothing */
if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
return;
/* mark the signal as pending */
sigm = (1L << (unsigned long)sig);
p->sigpending |= sigm;
/* if the signal is masked, do nothing further */
if ( (p->sigmask & sigm) != 0 )
return;
/* otherwise, make sure the process is awake */
if (p->wait_q && p->wait_q != READY_Q) {
short sr = spl7();
rm_q(p->wait_q, p);
add_q(READY_Q, p);
spl(sr);
}
}
/*
* check_sigs: see if we have any signals pending. if so,
* handle them.
*/
void
check_sigs()
{
ulong sigs, sigm;
int i;
short deliversig;
if (curproc->pid == 0) return;
top:
sigs = curproc->sigpending & ~(curproc->sigmask);
if (sigs) {
sigm = 2;
/* with tracing we need a mechanism to allow a signal to be delivered
* to the child (curproc); Fcntl(...TRACEGO...) passes a SIGNULL to indicate that we
* should really deliver the signal, hence its always safe to remove it
* from pending.
*/
deliversig = (curproc->sigpending & 1L);
curproc->sigpending &= ~1L;
for (i = 1; i < NSIG; i++) {
if (sigs & sigm) {
curproc->sigpending &= ~sigm;
if (curproc->ptracer && !deliversig) {
TRACE(("tracer being notified of signal %d", i));
stop(i);
/* the parent may reset our pending signals, so check again */
goto top;
} else {
ulong omask;
curproc->sigpending &= ~sigm;
omask = curproc->sigmask;
/* sigextra gives which extra signals should also be masked */
curproc->sigmask |= curproc->sigextra[i] | sigm;
handle_sig(i);
/*
* POSIX.1-3.3.4.2(723) "If and when the user's signal handler returns
* normally, the original signal mask is restored."
*
* BUG?: This unmasking could unmask a pending signal which we will not
* see this time around (if the signal number is less than i) and which
* was not pending when we started; should we detect this condition and
* loop around for a second try? POSIX only guarantees delivery of
* one signal per kernel entry, so this shouldn't really be a problem.
*/
curproc->sigmask = omask; /* unmask signals */
}
}
sigm = sigm << 1;
}
}
}
/*
* raise: cause a signal to be raised in the current process
*/
void
raise(sig)
int sig;
{
post_sig(curproc, sig);
check_sigs();
}
#ifdef EXCEPTION_SIGS
/* exception numbers corresponding to signals */
char excep_num[NSIG] =
{ 0, 0, 0, 0,
4, /* SIGILL == illegal instruction */
9, /* SIGTRAP == trace trap */
4, /* pretend SIGABRT is also illegal instruction */
8, /* SIGPRIV == privileged instruction exception */
5, /* SIGFPE == divide by zero */
0, 2, /* SIGBUS == bus error */
3 /* SIGSEGV == address error */
/* everything else gets zeros */
};
/* a "0" means we don't print a message when it happens -- typically the
user is expecting a synchronous signal, so we don't need to report it
*/
const char *signames[NSIG] = { 0,
0, 0, 0, "ILLEGAL INSTRUCTION", "TRACE TRAP",
0, "PRIVILEGE VIOLATION", "DIVISION BY ZERO", 0, "BUS ERROR",
"ADDRESS ERROR", "BAD SYSTEM CALL", 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, "CPU TIME EXHAUSTED", "FILE TOO BIG",
0, 0, 0, 0, 0
};
/*
* replaces the TOS "show bombs" routine: for now, print the name of the
* interrupt on the console, and save info on the crash in the appropriate
* system area
*/
void
bombs(sig)
int sig;
{
long *procinfo = (long *)0x380L;
int i;
CONTEXT *crash;
extern int no_mem_prot;
if (sig < 0 || sig > 31) {
ALERT("bombs(%d): sig out of range", sig);
}
else if (signames[sig]) {
if (!no_mem_prot && sig == SIGBUS) {
/* already reported by report_buserr */
} else {
ALERT("%s: User PC=%lx (basepage=%lx)",
signames[sig],
curproc->exception_pc, curproc->base);
}
/* save the processor state at crash time */
/* assumes that "crash time" is the context curproc->ctxt[SYSCALL] */
/* BUG: this is not true if the crash happened in the kernel; in the
* latter case, the crash context wasn't saved anywhere.
*/
crash = &curproc->ctxt[SYSCALL];
*procinfo++ = 0x12345678L; /* magic flag for valid info */
for (i = 0; i < 15; i++)
*procinfo++ = crash->regs[i];
*procinfo++ = curproc->exception_ssp;
*procinfo++ = ((long)excep_num[sig]) << 24L;
*procinfo = crash->usp;
/* we're also supposed to save some info from the supervisor stack. it's not
* clear what we should do for MiNT, since most of the stuff that used to be
* on the stack has been put in the CONTXT struct. Moreover, we don't want
* to crash because of an attempt to access illegal memory. Hence, we do
* nothing here...
*/
} else {
TRACE(("bombs(%d)", sig));
}
}
#endif
/*
* handle_sig: do whatever is appropriate to handle a signal
*/
static long unwound_stack = 0;
void
handle_sig(sig)
int sig;
{
long oldstack, newstack;
long *stack;
CONTEXT *call, oldsysctxt, newcurrent;
extern void sig_return();
if (curproc->sighandle[sig] == SIG_IGN)
return;
else if (curproc->sighandle[sig] == SIG_DFL) {
_default:
switch(sig) {
#if 0
/* Note: SIGNULL is filtered out in dossig.c and is never actually
* delivered (its only purpose for the user is to test for the existence of
* a process, it isn't a real signal). The kernel uses SIGNULL
* internally, but all such code does the signal handling "by hand"
* and so no default handling is necessary.
*/
case SIGNULL:
#endif
case SIGWINCH:
case SIGCHLD:
/* SIGFPE is divide by 0; TOS ignores this, so we will too */
case SIGFPE:
return; /* do nothing */
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
stop(sig);
return;
case SIGCONT:
curproc->sigpending &= ~STOPSIGS;
return;
/* here are the fatal signals. for SIGINT, we use p_term(-32) so that
* TOS programs that catch ^C via the vector at 0x400 and which expect
* TOS's error code (-32) to be sent will work. For most other signals,
* we p_term with an error code; for SIGKILL, we don't want to allow
* the program any chance to recover, so we call terminate() directly
* to avoid calling through to the user's terminate vector.
*/
case SIGINT: /* ^C */
if (curproc->domain == DOM_TOS) {
p_term(-32);
return;
}
/* otherwise, fall through */
default:
#ifdef EXCEPTION_SIGS
bombs(sig); /* tell the user what happened */
#endif
/* the "sigmask" check is in case a bus error happens in the user's
* term_vec